/**
  ******************************************************************************
  * @file    main.c
  * @author  Milandr Application Team
  * @version V1.1.1
  * @date    10/10/2022
  * @brief   This example shows how to configure SpaceWire in echo mode
  ******************************************************************************
  * <br><br>
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, MILANDR SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2024 Milandr</center></h2>
  */

/* Includes ------------------------------------------------------------------*/
#include "MDR1986VE8T.h"
#include "spec.h"

#include "mdr32f8_port.h"
#include "mdr32f8_clkctrl.h"
#include "system_1986ve8t.h"
#include "mdr32f8_spw.h"
#include "ringbuf.h"

#include "mdr32f8_dma.h"

/** @addtogroup __MDR32F8_StdPeriph_Examples
  * @{
  */

/** @addtogroup __MDR32F8_Board_For_MCU
  * @{
  */

/** @addtogroup SPW_Example_LoopBack_Mode
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define SIZE_BUF    1024    // Размер буфера приёма в 32-разрядных словах. При использовании USE_DMA_RX максимальный размер равен 2048
#define USE_DMA_RX          // Для приёма данных SPW используется DMA, канал 0. Иначе данные принимаются ядром в обработчике прерывания SPW_Handler

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
PORT_InitTypeDef PORT_InitStructure;
SPW_InitTypeDef SPW_InitStructure;

uint32_t status;                /* Статус контроллера SpaceWire              */

uint32_t ParityErrCnt;          /* Счётчик ошибок чётности                   */
uint32_t EscapeErrCnt;          /* Счётчик ошибок в ESC последовательности   */
uint32_t CreditErrCnt;          /* Счётчик ошибок кредитования               */
uint32_t TimeMarkCnt;           /* Счётчик принятых маркеров времени         */
uint32_t PacketRcvCnt;          /* Счётчик принятых пакетов                  */
uint32_t PacketSndCnt;          /* Счётчик отправленных пакетов              */

uint32_t msTicks;               /* Переменная для хранения значения миллисекунд  */
uint32_t TickCnt;               /* Счётчик для формирования маркеров времени     */
uint32_t Time;                  /* Значение маркера времени                      */

uint32_t cnt_eopnulldesc;       /* Счётчик принятых пустых EOP пакетов       */
uint32_t cnt_eppnulldesc;       /* Счётчик принятых пустых EPP пакетов       */
uint32_t cnt_eopdatadesc;       /* Счётчик принятых EOP пакетов с данными    */
uint32_t cnt_eppdatadesc;       /* Счётчик принятых EPP пакетов с данными    */
uint32_t packet_length;         /* Размер принятого пакетов в байтах         */

RingBuf_TypeDef receive_ringbuf;/* Структура кольцевого буфера для хранения принятых пакетов */
uint32_t rx_ringbuf[SIZE_BUF];  /* Массив для кольцевого буфера                              */
uint32_t packet_buf[SIZE_BUF];  /* Массив для обработки принятого пакета                     */


/* Private function prototypes -----------------------------------------------*/
void SPW_Handler(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
    /* ONLY REV2 MCU, errata 0015. Disable Power-on-Reset control. Hold the SW4 button down until operation complete */
    //POR_disable();
    
    // Key to access clock control 
    UNLOCK_UNIT(CLK_CNTR_key);
    // Key to access fault control
    UNLOCK_UNIT(FT_CNTR_key); 
    // Key to access BKP control
    UNLOCK_UNIT(BKP_key); 
     
    /* Set CLKCTRL to default */
    CLKCTRL_DeInit();
    /* Enable HSE0 clock */
    CLKCTRL_HSEconfig(CLKCTRL_HSE0_CLK_ON);
    
    //BKPCNTR_SRILOWconfig (BKPCNTR_SRILOW_upto_100MHz);
    BKPCNTR_SRILOWconfig(BKPCNTR_SRILOW_over_10kHz);  // If MCU is installed in a contact device
    
    /* Check HSE success, enable PLL0, check PLL0 success, select MAX_CLK src */
    while(CLKCTRL_HSEstatus(CLKCTRL_HSEn_STAT_HSE0_RDY) != SUCCESS){}

    /* Select PLL0 clk src, PLL0_N, PLL0_Q to get FINT = FIN*(PLLn_N)/(PLLn_Q+1) */
    CLKCTRL_CPU_PLLconfig(PLL0, CLKCTRL_PLLn_CLK_SELECT_HSE0div1, PLL_DIVQ_Q_1_DV, 12); // PLLn, SRC, Q, N; FINT = (10*12/1)/2 = 60 MHz
    while(CLKCTRL_CPU_PLLstatus(0) != SUCCESS){}
    
    CLKCTRL_MAX_CLKSelection(CLKCTRL_MAX_CLK_PLL0);
    SystemCoreClockUpdate();
    
    /* SPW clock configuration */
    CLKCTRL_PER0_CLKcmd(CLKCTRL_PER0_CLK_MDR_SPW0_EN, ENABLE);
    SPW_CLK_en(SPW_CLKdiv1);
        
    SPW_StructInit(&SPW_InitStructure);
     
    SPW_InitStructure.SPW_DIVCNT = 3;                   // Operating speed divider, SPW_SPEED = SPWCLK/DIVCNT = 60/3 = 20 Mbps
    SPW_InitStructure.SPW_DIVCNTDEF = 6;                // Divisor for starting speed, SPW_SPEED = SPWCLK/DIVCNTDEF = 60/6 = 10 Mbps
    SPW_InitStructure.SPW_Pause_Dsctime = 51;           // Number of CPU_CLK clocks for 850 ns interval
    SPW_InitStructure.SPW_Pause_Restime = 385;          // Number of CPU_CLK clocks for 6,4 us interval
    SPW_InitStructure.SPW_PHY_Out_En = ENABLE;          // Transceiver transmit permission
    SPW_InitStructure.SPW_PHY_SELR = ENABLE;            // Using an external stable resistor
    SPW_InitStructure.SPW_PHY_EN_BNG = ENABLE;          // Enable REF block for SPW
    SPW_InitStructure.SPW_Link_Ready_Started = ENABLE;  // Allow transition from the Ready state to the Running state
    SPW_InitStructure.SPW_Link_Autostart = DISABLE;     // Autostart mode
    SPW_Init(MDR_SPW0, &SPW_InitStructure);
    
#ifdef USE_DMA_RX        
    SPW_DMACmd(MDR_SPW0, SPW_DMARXEN, ENABLE);
    SPW_InitRxDMA(MDR_SPW0, rx_ringbuf, SIZE_BUF, DMA_RealChannel_0, ENABLE);
#else
    SPW_ITConfig (MDR_SPW0, SPW_IT_INTERXF34, ENABLE);
    NVIC_ClearPendingIRQ(SPW0_IRQn);
    NVIC_EnableIRQ(SPW0_IRQn);
#endif
    
    SPW_PHYCmd(MDR_SPW0, ENABLE);
    SPW_Cmd(MDR_SPW0, ENABLE);
    
    /* Init SysTick for time code */
    SysTick_Config(SystemCoreClock/1000);
    
    /* Init Ring Buffer */
    RingBuf_Init(&receive_ringbuf, rx_ringbuf, SIZE_BUF);
    
    /* Init start value of TickCnt */
    TickCnt = msTicks + 1000;
    
    /* Main cycle */
    while (1)
    {
        SPW_Handler();
    }
}

/* Реализует режим ЭХО: данные, полученные от внешнего устройства, передаются обратно */
void SPW_Handler (void)
{
    uint32_t i;
//................................................................................ 
/*  Определение состояния обмена данными в канале                               */
//................................................................................ 
    // Признак ошибки четности
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_PERR))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_PERR);
        ParityErrCnt++;
    }
    // Признак рассоединения с каналом связи
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_DSCERR))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_DSCERR);
        Time = 0;
    }
    // Признак ошибки в ESC последовательности
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_ESCERR))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_ESCERR);
        EscapeErrCnt++;
    }
    // Признак ошибки кредитования
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_CREDERR))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_CREDERR);
        CreditErrCnt++;
    }
    // Признак приема маркера времени
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_GOTTIME))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_GOTTIME);
        TimeMarkCnt++;
    }
    // Флаг завершения приёма пакета данных с признаком конца пакета
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_RXDESC))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_RXDESC);
        PacketRcvCnt++;
    }
    // Флаг завершения передачи данных с признаком конца пакета
    if(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_TXDESC))
    {
        SPW_ClearFlag(MDR_SPW0, SPW_FLAG_TXDESC);
        PacketSndCnt++;
    }
    
//..........................................................................................................................................	
/*  Синхронизация по времени. МК формирует маркер времени, каждый раз увеличивая его на 1 (до 64), чтобы поддерживать синхронизацию в сети */
//.......................................................................................................................................... 
    if(SPW_GetLinkState(MDR_SPW0) == SPW_State_Run) // Проверка состояния Run
    {
        if(TickCnt < msTicks) // Если превышен лимит ожидания
        {
            SPW_SetTimeTXMarker(MDR_SPW0, SPW_TIME_CODE, Time);
            SPW_StartTransmissionTimeTXMarker(MDR_SPW0); // Начать передачу маркера
            if(Time==63)
            {
                Time = 0;
            }
            else
            {
                Time++;
            }
            TickCnt += 1000;
        }
    }
//................................................................................
/*  Обработчик принимаемых пакетов (Режим ЭХО)                                              */
//................................................................................
    /* Проверка всех дескрипторов приема */
    for(i = 0; i < 16; i++)
    {
        if(SPW_IsRXDescReady(MDR_SPW0, i)) // Если дескриптор заполнен данными
        {
            if(SPW_GetRXDescPacketLength(MDR_SPW0, i) == 0) // Если поле данных в пакете пустое
            {   
                if(SPW_GetRXDescPacketType(MDR_SPW0, i) == SPW_EOP) // Если получен пакет EOP
                {  
                    // Получен пустой пакет EOP
                    cnt_eopnulldesc++;
                    // Отправить пустой пакет с маркером EOP
                    SPW_SendPacket(MDR_SPW0, SPW_EOP, NULL, 0);
                }
                else if(SPW_GetRXDescPacketType(MDR_SPW0, i) == SPW_EEP) // Если получен пакет EEP
                {
                    // Получен пустой пакет EEP
                    cnt_eppnulldesc++;
                    // Отправить пустой пакет с маркером EEP
                    SPW_SendPacket(MDR_SPW0, SPW_EEP, NULL, 0);
                }
            }
            else  // Если поле данных в пакета не пустое
            {
#ifndef USE_DMA_RX
                // Данные считываются в кольцевой буфер в обработчике прерывания от SpaceWire (INT_SPW0_Handler()). Прерывание возникает только при заполненности 3/4 буфера FIFORX.
                // Считывание всех оставшихся данных из буфера FIFORX, которые не были считаны в INT_SPW0_Handler(), перед обработкой дескриптора
                NVIC_DisableIRQ(SPW0_IRQn);
                
                while(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_RXNE))// Пока в FIFORX есть данные
                    RingBuf_WriteWord(&receive_ringbuf, SPW_ReceiveData(MDR_SPW0));
                
                NVIC_EnableIRQ(SPW0_IRQn);
#endif
                
                // Определение длины пакета в байтах
                packet_length = SPW_GetRXDescPacketLength(MDR_SPW0, i);
                
                if(SPW_GetRXDescPacketType(MDR_SPW0, i) == SPW_EOP) // Если получен пакет EOP
                {
                    // Получен пакет EOP
                    cnt_eopdatadesc++;
                    // Считать принятый пакет из кольцевого буфера
                    RingBuf_Read(&receive_ringbuf, packet_buf, packet_length);
                    // Отправить принятый пакет c маркером EOP
                    SPW_SendPacket(MDR_SPW0, SPW_EOP, packet_buf, packet_length);
                }
                else if(SPW_GetRXDescPacketType(MDR_SPW0, i) == SPW_EEP) // Если получен пакет EEP
                {
                    // Получен пакет EEP
                    cnt_eppdatadesc++;
                    // Считать принятый пакет из кольцевого буфера
                    RingBuf_Read(&receive_ringbuf, packet_buf, packet_length);
                    // Отправить принятый пакет c маркером EEP
                    SPW_SendPacket(MDR_SPW0, SPW_EEP, packet_buf, packet_length);
                }
            }
            SPW_ClearRXDesc(MDR_SPW0, i); // Очистка дескриптор приёмника RXDESC[i]
        }
    }
}

void SysTick_Handler(void)
{
    msTicks++;
}

#ifndef USE_DMA_RX
/* Обработчик прерываний от SpaceWire. Выполняет запись принятых данных в кольцевой буфер при заполненности 3/4 FIFORX*/
void INT_SPW0_Handler(void)
{
    while(SPW_GetFlagStatus(MDR_SPW0, SPW_FLAG_RXNE)) // Пока в FIFORX есть данные
        RingBuf_WriteWord(&receive_ringbuf, SPW_ReceiveData(MDR_SPW0));
}
#else
extern DMA_CtrlDataInitTypeDef DMA_PriCtrlStr_SPW_RX;
extern DMA_CtrlDataInitTypeDef DMA_AltCtrlStr_SPW_RX;

void DMA_DONE0_Handler(void)
{
    /* Reconfigure the inactive DMA data structure */
    if(DMA_GetFlagStatus(DMA_RealChannel_0, DMA_FLAG_CHNL_ALT) == RESET) // Worked out an alternative structure, now the main structure is working
    {
        DMA_CtrlInit(DMA_RealChannel_0, DMA_CTRL_DATA_ALTERNATE, &DMA_AltCtrlStr_SPW_RX);
    }
    else if(DMA_GetFlagStatus(DMA_RealChannel_0, DMA_FLAG_CHNL_ALT) == SET) // Worked out the main structure, now an alternative structure is working
    {
        DMA_CtrlInit(DMA_RealChannel_0, DMA_CTRL_DATA_PRIMARY, &DMA_PriCtrlStr_SPW_RX);
    }
}

#endif

//-----------------------------assert_param--------------------------------
#if (USE_ASSERT_INFO == 1)
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the source file ID and line number.
     Ex: printf("Wrong parameters value: file Id %d on line %d\r\n", file_id, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#elif (USE_ASSERT_INFO == 2)
void assert_failed(uint32_t file_id, uint32_t line, const uint8_t* expr)
{
  /* User can add his own implementation to report the source file ID, line number and
     expression text.
     Ex: printf("Wrong parameters value (%s): file Id %d on line %d\r\n", expr, file_id, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif /* USE_ASSERT_INFO */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/******************* (C) COPYRIGHT 2024 Milandr *******************************/

/* END OF FILE main.c */

